home *** CD-ROM | disk | FTP | other *** search
- {===EZDSLCOL==========================================================
-
- Part of the EZ Delphi Structures Library--the collection classes.
-
- EZDSLCOL is Copyright (c) 1995, 1996 by Julian M. Bucknall
-
- VERSION HISTORY
- 13Mar96 JMB 2.00 release for Delphi 2.0
- 18Jun95 JMB 1.00 initial release
- ======================================================================}
- { Copyright (c) 1995, 1996, Julian M. Bucknall. All Rights Reserved }
-
- unit EZDSLCol;
-
- {$I EZDSLDEF.INC}
- {---Place any compiler options you require here-----------------------}
-
-
- {---------------------------------------------------------------------}
- {$I EZDSLOPT.INC}
-
- interface
-
- uses
- SysUtils,
- {$IFDEF Win32}
- Windows,
- {$ELSE}
- WinTypes,
- WinProcs,
- {$ENDIF}
- Classes,
- EZDSLCts,
- EZDSLSup,
- EZDSLBse;
-
- const
- ezcPageElementCount = 92;
- ezcPageArrayElementCount = 10922;
- ezcMaxCount = ezcPageElementCount * ezcPageArrayElementCount;
-
- coIndexError = -1;
- coOverflow = -2;
-
- type
- PezcPage = ^TezcPage;
- TezcPage = array [0..pred(ezcPageElementCount)] of pointer;
-
- PezcPageItem = ^TezcPageItem;
- TezcPageItem = record
- UsedItems : integer;
- Items : PezcPage;
- end;
-
- PezcPageArray = ^TezcPageArray;
- TezcPageArray = array [0..pred(ezcPageArrayElementCount)] of TezcPageItem;
-
- TEZCollection = class(TAbstractContainer)
- private
- PA : PezcPageArray;
- SizeOfPA : Cardinal;
- ItemsInPA : integer;
- MaxItemsInPA : integer;
-
- CacheIndex : longint;
- CachePageNum : integer;
- CacheInxInPage : integer;
-
- protected
- function GetLimit : longint;
-
- procedure AddPageItem(AtIndex : integer);
- procedure DeletePageItem(AtIndex : integer);
- function GetPageGivenIndex(Index : longint;
- var InxInPage : integer) : integer;
- procedure GrowPageArray(NewNumElements : integer);
- procedure ValidateIndex(Index : longint);
-
- public
- constructor Create(DataOwner : boolean); override;
- constructor Clone(Source : TAbstractContainer;
- DataOwner : boolean; NewCompare : TCompareFunc); override;
- destructor Destroy; override;
-
- procedure Assign(Source : TPersistent); override;
-
- procedure Empty; override;
-
- function At(Index : longint) : pointer;
- procedure AtDelete(Index : longint);
- procedure AtFree(Index : longint);
- procedure AtInsert(Index : longint; Item : pointer);
- procedure AtPut(Index : longint; Item : pointer);
- procedure Delete(Item : pointer);
- procedure DeleteAll;
- procedure Free(Item : pointer);
- procedure FreeAll;
- function IndexOf(Item : pointer) : longint; virtual;
- procedure Insert(Item : pointer); virtual;
- function Iterate(Action : TIterator; Backwards : boolean;
- ExtraData : pointer) : pointer;
- procedure Pack;
-
- property Limit : longint
- read GetLimit;
-
- property Items[Index : longint] : pointer
- read At
- write AtPut;
- default;
- end;
-
- TEZSortedCollection = class(TEZCollection)
- public
- function IndexOf(Item : pointer) : longint; override;
- procedure Insert(Item : pointer); override;
- function Search(Item : pointer; var Index : longint) : boolean; virtual;
- end;
-
- TEZStringCollection = class(TEZSortedCollection)
- public
- constructor Create(DataOwner : boolean); override;
- end;
-
- TEZStrZCollection = class(TEZSortedCollection)
- public
- constructor Create(DataOwner : boolean); override;
- end;
-
- implementation
-
- procedure RaiseCollError(Code : integer);
- var
- SCode : integer;
- begin
- case Code of
- coIndexError : SCode := escIndexError;
- coOverflow : SCode := escTooManyItems;
- end;
- EZDSLSup.RaiseError(SCode);
- end;
-
- {===TEZCollection creation/destruction===============================}
- constructor TEZCollection.Create(DataOwner : boolean);
- begin
- NodeSize := 0;
- inherited Create(DataOwner);
-
- GrowPageArray(1);
- AddPageItem(0);
- end;
- {--------}
- constructor TEZCollection.Clone(Source : TAbstractContainer;
- DataOwner : boolean; NewCompare : TCompareFunc);
- var
- OldColl : TEZCollection absolute Source;
- NewData : pointer;
- i : longint;
- begin
- Create(DataOwner);
- Compare := NewCompare;
- DupData := OldColl.DupData;
- DisposeData := OldColl.DisposeData;
-
- if not (Source is TEZCollection) then
- RaiseError(escBadSource);
-
- if not OldColl.IsEmpty then
- for i := 0 to pred(OldColl.Count) do
- begin
- NewData := DupData(OldColl.Items[i]);
- try
- Insert(NewData);
- except
- DisposeData(NewData);
- raise;
- end;
- end;
- end;
- {--------}
- destructor TEZCollection.Destroy;
- begin
- inherited Destroy;
- if Assigned(PA) then
- begin
- DeletePageItem(0);
- FreeMem(PA, SizeOfPA);
- end;
- end;
- {====================================================================}
-
-
- {===TEZCollection helper methods=====================================}
- procedure TEZCollection.AddPageItem(AtIndex : integer);
- var
- NewPage : PezcPage;
- NewMax : integer;
- begin
- {$IFDEF DEBUG}
- if (AtIndex > ItemsInPA) then
- raise Exception.Create('Bad AtIndex parm to AddPageItem');
- {$ENDIF}
- if (ItemsInPA = MaxItemsInPA) then
- if (MaxItemsInPA < ezcPageArrayElementCount) then
- begin
- case MaxItemsInPA of
- 1 : NewMax := 2;
- 2 : NewMax := 4;
- 4 : NewMax := 8;
- 8 : NewMax := 16;
- else
- NewMax := MaxItemsInPA + 16;
- if (NewMax > ezcPageArrayElementCount) then
- NewMax := ezcPageArrayElementCount;
- end;{case}
- GrowPageArray(NewMax);
- end
- else
- begin
- Pack;
- if (ItemsInPA = ezcPageArrayElementCount) then
- RaiseCollError(coOverflow);
- end;
- GetMem(NewPage, ezcPageElementCount * sizeof(pointer));
- {$IFDEF DEBUG}
- FillChar(NewPage^, ezcPageElementCount * sizeof(pointer), $CC);
- {$ENDIF}
- if (AtIndex < ItemsInPA) then
- Move(PA^[AtIndex], PA^[succ(AtIndex)], (ItemsInPA - AtIndex) * sizeof(TezcPageItem));
- with PA^[AtIndex] do
- begin
- UsedItems := 0;
- Items := NewPage;
- end;
- inc(ItemsInPA);
- end;
- {--------}
- procedure TEZCollection.DeletePageItem(AtIndex : integer);
- begin
- {$IFDEF DEBUG}
- if (AtIndex >= ItemsInPA) then
- raise Exception.Create('Bad AtIndex parm to DeletePageItem');
- {$ENDIF}
- with PA^[AtIndex] do
- FreeMem(Items, ezcPageElementCount * sizeof(pointer));
- dec(ItemsInPA);
- if (AtIndex < ItemsInPA) then
- Move(PA^[succ(AtIndex)], PA^[AtIndex], (ItemsInPA - AtIndex) * sizeof(TezcPageItem));
- end;
- {--------}
- function TEZCollection.GetPageGivenIndex(Index : longint;
- var InxInPage : integer) : integer;
- const
- SizeOfPageItem = sizeof(TezcPageItem);
- var
- PageNum : integer;
- StartIndex : longint;
- GoForward : boolean;
- begin
- if (Index = CacheIndex) then
- begin
- Result := CachePageNum;
- InxInPage := CacheInxInPage;
- Exit;
- end;
- if (Index < CacheIndex) then
- if ((Index * 2) <= CacheIndex) then
- begin
- {Index is closer to 0 than CacheIndex}
- PageNum := 0;
- StartIndex := Index;
- GoForward := true;
- end
- else
- begin
- {Index is closer to CacheIndex than 0}
- PageNum := CachePageNum;
- StartIndex :=
- (CacheIndex - CacheInxInPage + PA^[CachePageNum].UsedItems) -
- Index;
- GoForward := false;
- end
- else {Index > CacheIndex}
- if (Index - CacheIndex) <= (Count - Index - 1) then
- begin
- {Index is closer to CacheIndex than Count}
- PageNum := CachePageNum;
- StartIndex := Index - (CacheIndex - CacheInxInPage);
- GoForward := true;
- end
- else
- begin
- {Index is closer to Count than CacheIndex}
- PageNum := pred(ItemsInPA);
- StartIndex := Count - Index;
- GoForward := false;
- end;
- {$IFDEF Win32}
- if GoForward then
- asm
- mov edx, Self
- mov edx, [edx].TEZCollection.PA
-
- mov ecx, PageNum {This assumes sizeof(TezcPageItem)=8}
- mov eax, ecx
- shl eax, 3
- add edx, eax
-
- mov eax, StartIndex
- @@NextPage:
- sub eax, [edx].TezcPageItem.UsedItems
- jl @@FoundIt
- inc ecx
- add edx, SizeOfPageItem
- jmp @@NextPage
- @@FoundIt:
- add eax, [edx].TezcPageItem.UsedItems
- mov edx, InxInPage
- mov [edx], eax
- mov @Result, ecx
- end
- else {go backwards}
- asm
- mov edx, Self
- mov edx, [edx].TEZCollection.PA
-
- mov ecx, PageNum {This assumes sizeof(TezcPageItem)=8}
- mov eax, ecx
- shl eax, 3
- add edx, eax
-
- mov eax, StartIndex
- @@NextPage:
- sub eax, [edx].TezcPageItem.UsedItems
- jl @@FoundIt
- je @@FoundItAsZero
- dec ecx
- sub edx, SizeOfPageItem
- jmp @@NextPage
- @@FoundIt:
- neg eax
- @@FoundItAsZero:
- mov edx, InxInPage
- mov [edx], eax
- mov @Result, ecx
- end;
- {$ELSE}
- if GoForward then
- asm
- mov si, ds {SI stores the Delphi data segment}
- lds di, Self
- lds di, [di].TEZCollection.PA
-
- mov cx, PageNum {This assumes sizeof(TezcPageItem)=6}
- mov ax, cx
- shl ax, 1
- add ax, cx
- shl ax, 1
- add di, ax
-
- xor bx, bx
- mov dx, StartIndex.Word[2]
- mov ax, StartIndex.Word[0]
- @@NextPage:
- sub ax, [di].TezcPageItem.UsedItems
- sbb dx, bx
- jl @@FoundIt
- inc cx
- add di, SizeOfPageItem
- jmp @@NextPage
- @@FoundIt:
- add ax, [di].TezcPageItem.UsedItems
- lds di, InxInPage
- mov [di], ax
- mov ds, si
- mov @Result, cx
- end
- else
- asm
- push ds
- lds di, Self
- lds di, [di].TEZCollection.PA
-
- mov cx, PageNum {This assumes sizeof(TezcPageItem)=6}
- mov ax, cx
- shl ax, 1
- add ax, cx
- shl ax, 1
- add di, ax
-
- xor bx, bx
- mov dx, StartIndex.Word[2]
- mov ax, StartIndex.Word[0]
- @@NextPage:
- sub ax, [di].TezcPageItem.UsedItems
- sbb dx, bx
- jl @@FoundIt
- mov si, ax
- or si, dx
- je @@FoundItAsZero
- dec cx
- sub di, SizeOfPageItem
- jmp @@NextPage
- @@FoundIt:
- neg ax
- @@FoundItAsZero:
- lds di, InxInPage
- mov [di], ax
- pop ds
- mov @Result, cx
- end;
- {$ENDIF}
- CacheIndex := Index;
- CachePageNum := Result;
- CacheInxInPage := InxInPage;
- end;
- {--------}
- procedure TEZCollection.GrowPageArray(NewNumElements : integer);
- var
- NewSize : Cardinal;
- NewPA : PezcPageArray;
- begin
- NewSize := NewNumElements * sizeof(TezcPageItem);
- GetMem(NewPA, NewSize);
- {$IFDEF DEBUG}
- FillChar(NewPA^, NewSize, $CC);
- {$ENDIF}
- if Assigned(PA) then
- begin
- Move(PA^, NewPA^, ItemsInPA * sizeof(TezcPageItem));
- FreeMem(PA, SizeOfPA);
- end;
- PA := NewPA;
- SizeOfPA := NewSize;
- MaxItemsInPA := NewNumElements;
- end;
- {--------}
- procedure TEZCollection.ValidateIndex(Index : longint);
- begin
- if (Index < 0) or (Index >= Count) then
- RaiseCollError(coIndexError);
- end;
- {====================================================================}
-
-
- {===TEZCollection item access========================================}
- function TEZCollection.At(Index : longint) : pointer;
- var
- PageNum : integer;
- InxInPage : integer;
- begin
- ValidateIndex(Index);
- PageNum := GetPageGivenIndex(Index, InxInPage);
- Result := PA^[PageNum].Items^[InxInPage];
- end;
- {--------}
- procedure TEZCollection.AtPut(Index : longint; Item : pointer);
- var
- PageNum : integer;
- InxInPage : integer;
- begin
- ValidateIndex(Index);
- PageNum := GetPageGivenIndex(Index, InxInPage);
- PA^[PageNum].Items^[InxInPage] := Item;
- end;
- {====================================================================}
-
-
- {===TEZCollection property access====================================}
- function TEZCollection.GetLimit : longint;
- begin
- Result := longint(MaxItemsInPA) * ezcPageElementCount;
- end;
- {====================================================================}
-
-
- {===TEZCollection methods============================================}
- procedure TEZCollection.Assign(Source : TPersistent);
- var
- Src : TEZCollection absolute Source;
- NewData : pointer;
- i : longint;
- begin
- if not (Source is TEZCollection) then
- Exit;
- Empty;
- FIsDataOwner := Src.IsDataOwner;
- Compare := Src.Compare;
- DupData := Src.DupData;
- DisposeData := Src.DisposeData;
- if not Src.IsEmpty then
- for i := 0 to pred(Src.Count) do
- begin
- NewData := DupData(Src.Items[i]);
- try
- Insert(NewData);
- except
- DisposeData(NewData);
- end;
- end;
- end;
- {--------}
- procedure TEZCollection.AtDelete(Index : longint);
- var
- PageNum : integer;
- InxInPage : integer;
- begin
- ValidateIndex(Index);
- PageNum := GetPageGivenIndex(Index, InxInPage);
- dec(FCount);
- with PA^[PageNum] do
- begin
- dec(UsedItems);
- if (UsedItems = 0) then
- begin
- if (ItemsInPA > 1) then
- DeletePageItem(PageNum);
- end
- else if (InxInPage < UsedItems) then
- Move(Items^[succ(InxInPage)], Items^[InxInPage],
- (UsedItems - InxInPage) * sizeof(pointer));
- end;
- CacheIndex := 0;
- CachePageNum := 0;
- CacheInxInPage := 0;
- end;
- {--------}
- procedure TEZCollection.AtFree(Index : longint);
- begin
- if IsDataOwner then
- DisposeData(Items[Index]);
- AtDelete(Index);
- end;
- {--------}
- procedure TEZCollection.AtInsert(Index : longint; Item : pointer);
- const
- HalfPageCount = ezcPageElementCount div 2;
- var
- PageNum : integer;
- InxInPage : integer;
- AddingAtEnd : boolean;
- begin
- {maximum count check}
- if (Count = ezcMaxCount) then
- RaiseCollError(coOverflow);
- {take care of special case-adding at end}
- if (Index = Count) then
- begin
- AddingAtEnd := true;
- PageNum := pred(ItemsInPA);
- InxInPage := PA^[PageNum].UsedItems;
- end
- {otherwise work out where to add it}
- else
- begin
- ValidateIndex(Index);
- AddingAtEnd := false;
- PageNum := GetPageGivenIndex(Index, InxInPage);
- end;
-
- {do we need a new page?}
- if (PA^[PageNum].UsedItems = ezcPageElementCount) then
- begin
- {add a new page after ours}
- AddPageItem(succ(PageNum));
- {if we are adding to the end, patch up the page number and index}
- if AddingAtEnd then
- begin
- PageNum := succ(PageNum);
- InxInPage := 0;
- end
- {if we were not adding at end, split the old page in two for efficiency}
- else
- begin
- Move(PA^[PageNum].Items^[HalfPageCount],
- PA^[succ(PageNum)].Items^[0],
- HalfPageCount * sizeof(pointer));
- PA^[PageNum].UsedItems := HalfPageCount;
- PA^[succ(PageNum)].UsedItems := HalfPageCount;
- if (InxInPage >= HalfPageCount) then
- begin
- dec(InxInPage, HalfPageCount);
- inc(PageNum);
- end;
- end;
- end;
-
- {insert the item now}
- with PA^[PageNum] do
- begin
- if (InxInPage < UsedItems) then
- Move(Items^[InxInPage], Items^[succ(InxInPage)],
- (UsedItems - InxInPage) * sizeof(pointer));
- Items^[InxInPage] := Item;
- inc(UsedItems);
- end;
- inc(FCount);
- CacheIndex := Index;
- CachePageNum := PageNum;
- CacheInxInPage := InxInPage;
- end;
- {--------}
- procedure TEZCollection.Delete(Item : pointer);
- var
- Index : longint;
- begin
- Index := IndexOf(Item);
- if (Index <> -1) then
- AtDelete(Index);
- end;
- {--------}
- procedure TEZCollection.DeleteAll;
- var
- i : integer;
- begin
- for i := pred(ItemsInPA) downto 1 do
- DeletePageItem(i);
- PA^[0].UsedItems := 0;
- FCount := 0;
- CacheIndex := 0;
- CachePageNum := 0;
- CacheInxInPage := 0;
- end;
- {--------}
- procedure TEZCollection.Empty;
- begin
- FreeAll;
- end;
- {--------}
- procedure TEZCollection.Free(Item : pointer);
- var
- Index : longint;
- begin
- Index := IndexOf(Item);
- if (Index <> -1) then
- AtFree(Index);
- end;
- {--------}
- procedure TEZCollection.FreeAll;
- var
- PageNum : integer;
- Inx : integer;
- begin
- if IsDataOwner then
- for PageNum := 0 to pred(ItemsInPA) do
- for Inx := 0 to pred(PA^[PageNum].UsedItems) do
- DisposeData(PA^[PageNum].Items^[Inx]);
- DeleteAll;
- end;
- {--------}
- function TEZCollection.IndexOf(Item : pointer) : longint;
- var
- PageNum : integer;
- Inx : integer;
- begin
- Result := -1;
- for PageNum := 0 to pred(ItemsInPA) do
- with PA^[PageNum] do
- for Inx := 0 to pred(UsedItems) do
- begin
- inc(Result);
- if (Items^[Inx] = Item) then
- begin
- CacheIndex := Result;
- CachePageNum := PageNum;
- CacheInxInPage := Inx;
- Exit;
- end;
- end;
- Result := -1;
- end;
- {--------}
- procedure TEZCollection.Insert(Item : pointer);
- begin
- AtInsert(Count, Item);
- end;
- {--------}
- function TEZCollection.Iterate(Action : TIterator;
- Backwards : boolean;
- ExtraData : pointer) : pointer;
- var
- PageNum : integer;
- Inx : integer;
- begin
- if Backwards then
- begin
- for PageNum := pred(ItemsInPA) downto 0 do
- with PA^[PageNum] do
- for Inx := pred(UsedItems) downto 0 do
- if not Action(Self, Items^[Inx], ExtraData) then
- begin
- Result := Items^[Inx];
- Exit;
- end;
- end
- else
- begin
- for PageNum := 0 to pred(ItemsInPA) do
- with PA^[PageNum] do
- for Inx := 0 to pred(UsedItems) do
- if not Action(Self, Items^[Inx], ExtraData) then
- begin
- Result := Items^[Inx];
- Exit;
- end;
- end;
- Result := nil;
- end;
- {--------}
- procedure TEZCollection.Pack;
- var
- FromPage : integer;
- ToPage : integer;
- ItemsToGo : integer;
- ItemsInToPage : integer;
- ItemsInFromPage : integer;
- StillPacking : boolean;
- begin
- if (ItemsInPA = 1) then Exit;
- ToPage := -1;
- FromPage := 1;
- StillPacking := true;
- while StillPacking do
- begin
- inc(ToPage);
- ItemsInToPage := PA^[ToPage].UsedItems;
- ItemsToGo := ezcPageElementCount - ItemsInToPage;
- if (FromPage <= ToPage) then
- begin
- FromPage := succ(ToPage);
- if (FromPage = ItemsInPA) then
- StillPacking := false;
- end;
- while StillPacking and (ItemsToGo > 0) do
- begin
- ItemsInFromPage := PA^[FromPage].UsedItems;
- if (ItemsInFromPage <= ItemsToGo) then
- begin
- Move(PA^[FromPage].Items^[0], PA^[ToPage].Items^[ItemsInToPage],
- ItemsInFromPage * sizeof(pointer));
- inc(ItemsInToPage, ItemsInFromPage);
- PA^[ToPage].UsedItems := ItemsInToPage;
- dec(ItemsToGo, ItemsInFromPage);
- PA^[FromPage].UsedItems := 0;
- inc(FromPage);
- if (FromPage = ItemsInPA) then
- StillPacking := false;
- end
- else
- begin
- Move(PA^[FromPage].Items^[0], PA^[ToPage].Items^[ItemsInToPage],
- (ItemsToGo * sizeof(pointer)));
- PA^[ToPage].UsedItems := ezcPageElementCount;
- Move(PA^[FromPage].Items^[ItemsToGo], PA^[FromPage].Items^[0],
- (ItemsInFromPage - ItemsToGo) * sizeof(pointer));
- PA^[FromPage].UsedItems := ItemsInFromPage - ItemsToGo;
- ItemsToGo := 0;
- end
- end;
- end;
- if (ToPage < pred(ItemsInPA)) then
- begin
- for FromPage := pred(ItemsInPA) downto succ(ToPage) do
- DeletePageItem(FromPage);
- GrowPageArray(ItemsInPA);
- end;
- CacheIndex := 0;
- CachePageNum := 0;
- CacheInxInPage := 0;
- end;
- {====================================================================}
-
-
- {====================================================================}
- function TEZSortedCollection.IndexOf(Item : pointer) : longint;
- var
- Index : longint;
- begin
- if Search(Item, Index) then
- Result := Index
- else
- Result := -1;
- end;
- {--------}
- procedure TEZSortedCollection.Insert(Item : pointer);
- var
- Index : longint;
- begin
- if not Search(Item, Index) then
- AtInsert(Index, Item)
- else
- RaiseError(escInsertDup);
- end;
- {--------}
- function TEZSortedCollection.Search(Item : pointer; var Index : longint) : boolean;
- var
- L, R, M : longint;
- PageNum : integer;
- InxInPage : integer;
- CompResult : integer;
- begin
- {check the obvious case}
- if (Count = 0) then
- begin
- Result := false;
- Index := 0;
- Exit;
- end;
- {standard binary search: Algorithms by Sedgewick}
- L := 0;
- R := pred(Count);
- repeat
- M := (L + R) div 2;
- PageNum := GetPageGivenIndex(M, InxInPage);
- CompResult := Compare(Item, PA^[PageNum].Items^[InxInPage]);
- if (CompResult = 0) then
- begin
- Result := true;
- Index := M;
- Exit;
- end
- else if (CompResult < 0) then
- R := M - 1
- else
- L := M + 1;
- until (L > R);
- Result := false;
- if (CompResult > 0) then
- Index := M + 1
- else
- Index := M;
- end;
- {====================================================================}
-
- {===TEZStringCollection==============================================}
- constructor TEZStringCollection.Create(DataOwner : boolean);
- begin
- inherited Create(DataOwner);
- Compare := EZStrCompare;
- DupData := EZStrDupData;
- DisposeData := EZStrDisposeData;
- end;
- {====================================================================}
-
- {===TEZStrZCollection================================================}
- constructor TEZStrZCollection.Create(DataOwner : boolean);
- begin
- inherited Create(DataOwner);
- Compare := EZStrZCompare;
- DupData := EZStrZDupData;
- DisposeData := EZStrZDisposeData;
- end;
- {====================================================================}
-
- end.
-